From f50a3af1b7a24836c784797484cae14052cbfcdd Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Mon, 11 Nov 2013 18:04:34 -0500 Subject: [PATCH] Handle recursion from motion event handlers If a motion event handler (or other handler running from the flush-events phase of the frame clock) recursed the main loop then flushing wouldn't complete until after the recursed main loop returned, and various aspects of the state would get out of sync. To fix this, change flushing of the event queue to simply mark events as ready to flush, and let normal event delivery handle the rest. https://bugzilla.gnome.org/show_bug.cgi?id=705176 --- gdk/broadway/gdkeventsource.c | 10 +++------- gdk/gdkdisplay.c | 24 ++---------------------- gdk/gdkdisplayprivate.h | 2 -- gdk/gdkevents.c | 20 ++++++++++++++++---- gdk/gdkinternals.h | 9 ++++++++- gdk/gdkwindow.c | 2 +- gdk/quartz/gdkeventloop-quartz.c | 4 ++-- gdk/wayland/gdkeventsource.c | 4 ++-- gdk/win32/gdkevents-win32.c | 4 ++-- gdk/x11/gdkeventsource.c | 4 ++-- 10 files changed, 38 insertions(+), 45 deletions(-) diff --git a/gdk/broadway/gdkeventsource.c b/gdk/broadway/gdkeventsource.c index e6c0a7f5a6..d7cdede0a4 100644 --- a/gdk/broadway/gdkeventsource.c +++ b/gdk/broadway/gdkeventsource.c @@ -62,10 +62,7 @@ gdk_event_source_prepare (GSource *source, *timeout = -1; - if (display->event_pause_count > 0) - retval = FALSE; - else - retval = (_gdk_event_queue_find_first (display) != NULL); + retval = (_gdk_event_queue_find_first (display) != NULL); gdk_threads_leave (); @@ -80,9 +77,8 @@ gdk_event_source_check (GSource *source) gdk_threads_enter (); - if (event_source->display->event_pause_count > 0) - retval = FALSE; - else if (event_source->event_poll_fd.revents & G_IO_IN) + if (event_source->display->event_pause_count > 0 || + event_source->event_poll_fd.revents & G_IO_IN) retval = (_gdk_event_queue_find_first (event_source->display) != NULL); else retval = FALSE; diff --git a/gdk/gdkdisplay.c b/gdk/gdkdisplay.c index 10928698fd..f19319d538 100644 --- a/gdk/gdkdisplay.c +++ b/gdk/gdkdisplay.c @@ -321,10 +321,8 @@ gdk_display_get_event (GdkDisplay *display) { g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); - if (display->event_pause_count > 0) - return NULL; - - GDK_DISPLAY_GET_CLASS (display)->queue_events (display); + if (display->event_pause_count == 0) + GDK_DISPLAY_GET_CLASS (display)->queue_events (display); return _gdk_event_unqueue (display); } @@ -2032,24 +2030,6 @@ _gdk_display_unpause_events (GdkDisplay *display) display->event_pause_count--; } -void -_gdk_display_flush_events (GdkDisplay *display) -{ - display->flushing_events = TRUE; - - while (TRUE) - { - GdkEvent *event = _gdk_event_unqueue (display); - if (event == NULL) - break; - - _gdk_event_emit (event); - gdk_event_free (event); - } - - display->flushing_events = FALSE; -} - void _gdk_display_event_data_copy (GdkDisplay *display, const GdkEvent *event, diff --git a/gdk/gdkdisplayprivate.h b/gdk/gdkdisplayprivate.h index 365b429c33..df79f59993 100644 --- a/gdk/gdkdisplayprivate.h +++ b/gdk/gdkdisplayprivate.h @@ -116,7 +116,6 @@ struct _GdkDisplay guint event_pause_count; /* How many times events are blocked */ guint closed : 1; /* Whether this display has been closed */ - guint flushing_events : 1; /* Inside gdk_display_flush_events */ GArray *touch_implicit_grabs; GHashTable *device_grabs; @@ -300,7 +299,6 @@ void _gdk_display_pointer_info_foreach (GdkDisplay *display gulong _gdk_display_get_next_serial (GdkDisplay *display); void _gdk_display_pause_events (GdkDisplay *display); void _gdk_display_unpause_events (GdkDisplay *display); -void _gdk_display_flush_events (GdkDisplay *display); void _gdk_display_event_data_copy (GdkDisplay *display, const GdkEvent *event, GdkEvent *new_event); diff --git a/gdk/gdkevents.c b/gdk/gdkevents.c index 83c313cd34..86ea3570fb 100644 --- a/gdk/gdkevents.c +++ b/gdk/gdkevents.c @@ -88,20 +88,20 @@ _gdk_event_queue_find_first (GdkDisplay *display) GList *tmp_list; GList *pending_motion = NULL; - if (display->event_pause_count > 0) - return NULL; + gboolean paused = display->event_pause_count > 0; tmp_list = display->queued_events; while (tmp_list) { GdkEventPrivate *event = tmp_list->data; - if (!(event->flags & GDK_EVENT_PENDING)) + if ((event->flags & GDK_EVENT_PENDING) == 0 && + (!paused || (event->flags & GDK_EVENT_FLUSHED) != 0)) { if (pending_motion) return pending_motion; - if (event->event.type == GDK_MOTION_NOTIFY && !display->flushing_events) + if (event->event.type == GDK_MOTION_NOTIFY && (event->flags & GDK_EVENT_FLUSHED) == 0) pending_motion = tmp_list; else return tmp_list; @@ -321,6 +321,18 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display) } } +void +_gdk_event_queue_flush (GdkDisplay *display) +{ + GList *tmp_list; + + for (tmp_list = display->queued_events; tmp_list; tmp_list = tmp_list->next) + { + GdkEventPrivate *event = tmp_list->data; + event->flags |= GDK_EVENT_FLUSHED; + } +} + /** * gdk_event_handler_set: * @func: the function to call to handle events from GDK. diff --git a/gdk/gdkinternals.h b/gdk/gdkinternals.h index b2456662ec..bebdec03b7 100644 --- a/gdk/gdkinternals.h +++ b/gdk/gdkinternals.h @@ -156,7 +156,13 @@ typedef enum * 1) touch events emulating pointer events * 2) pointer events being emulated by a touch sequence. */ - GDK_EVENT_POINTER_EMULATED = 1 << 1 + GDK_EVENT_POINTER_EMULATED = 1 << 1, + + /* When we are ready to draw a frame, we pause event delivery, + * mark all events in the queue with this flag, and deliver + * only those events until we finish the frame. + */ + GDK_EVENT_FLUSHED = 1 << 2 } GdkEventFlags; struct _GdkEventPrivate @@ -305,6 +311,7 @@ GList* _gdk_event_queue_insert_before(GdkDisplay *display, GdkEvent *event); void _gdk_event_queue_handle_motion_compression (GdkDisplay *display); +void _gdk_event_queue_flush (GdkDisplay *display); void _gdk_event_button_generate (GdkDisplay *display, GdkEvent *event); diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 8c121ed741..f6330e2d7e 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -10696,7 +10696,7 @@ gdk_window_flush_events (GdkFrameClock *clock, window = GDK_WINDOW (data); display = gdk_window_get_display (window); - _gdk_display_flush_events (display); + _gdk_event_queue_flush (display); _gdk_display_pause_events (display); gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS); diff --git a/gdk/quartz/gdkeventloop-quartz.c b/gdk/quartz/gdkeventloop-quartz.c index 2c1ca0b467..bc1ecbb3d8 100644 --- a/gdk/quartz/gdkeventloop-quartz.c +++ b/gdk/quartz/gdkeventloop-quartz.c @@ -647,7 +647,7 @@ gdk_event_prepare (GSource *source, *timeout = -1; if (_gdk_display->event_pause_count > 0) - retval = FALSE; + retval = _gdk_event_queue_find_first (_gdk_display) != NULL; else retval = (_gdk_event_queue_find_first (_gdk_display) != NULL || _gdk_quartz_event_loop_check_pending ()); @@ -665,7 +665,7 @@ gdk_event_check (GSource *source) gdk_threads_enter (); if (_gdk_display->event_pause_count > 0) - retval = FALSE; + retval = _gdk_event_queue_find_first (_gdk_display) != NULL; else retval = (_gdk_event_queue_find_first (_gdk_display) != NULL || _gdk_quartz_event_loop_check_pending ()); diff --git a/gdk/wayland/gdkeventsource.c b/gdk/wayland/gdkeventsource.c index 9dda53a1ff..11e7b1ef59 100644 --- a/gdk/wayland/gdkeventsource.c +++ b/gdk/wayland/gdkeventsource.c @@ -40,7 +40,7 @@ gdk_event_source_prepare(GSource *base, gint *timeout) *timeout = -1; if (source->display->event_pause_count > 0) - return FALSE; + return _gdk_event_queue_find_first (source->display) != NULL; /* We have to add/remove the GPollFD if we want to update our * poll event mask dynamically. Instead, let's just flush all @@ -64,7 +64,7 @@ gdk_event_source_check(GSource *base) GdkWaylandEventSource *source = (GdkWaylandEventSource *) base; if (source->display->event_pause_count > 0) - return FALSE; + return _gdk_event_queue_find_first (source->display) != NULL; return _gdk_event_queue_find_first (source->display) != NULL || source->pfd.revents; diff --git a/gdk/win32/gdkevents-win32.c b/gdk/win32/gdkevents-win32.c index e7524d0bc8..2f751ee0b9 100644 --- a/gdk/win32/gdkevents-win32.c +++ b/gdk/win32/gdkevents-win32.c @@ -3328,7 +3328,7 @@ gdk_event_prepare (GSource *source, *timeout = -1; if (_gdk_display->event_pause_count > 0) - retval = FALSE; + retval =_gdk_event_queue_find_first (_gdk_display) != NULL; else retval = (_gdk_event_queue_find_first (_gdk_display) != NULL || (modal_win32_dialog == NULL && @@ -3347,7 +3347,7 @@ gdk_event_check (GSource *source) gdk_threads_enter (); if (_gdk_display->event_pause_count > 0) - retval = FALSE; + retval = gdk_event_queue_find_first (_gdk_display) != NULL; else if (event_poll_fd.revents & G_IO_IN) retval = (_gdk_event_queue_find_first (_gdk_display) != NULL || (modal_win32_dialog == NULL && diff --git a/gdk/x11/gdkeventsource.c b/gdk/x11/gdkeventsource.c index 4b1a546945..7fff28f0c2 100644 --- a/gdk/x11/gdkeventsource.c +++ b/gdk/x11/gdkeventsource.c @@ -278,7 +278,7 @@ gdk_event_source_prepare (GSource *source, *timeout = -1; if (display->event_pause_count > 0) - retval = FALSE; + retval = _gdk_event_queue_find_first (display) != NULL; else retval = (_gdk_event_queue_find_first (display) != NULL || gdk_check_xpending (display)); @@ -297,7 +297,7 @@ gdk_event_source_check (GSource *source) gdk_threads_enter (); if (event_source->display->event_pause_count > 0) - retval = FALSE; + retval = _gdk_event_queue_find_first (event_source->display) != NULL; else if (event_source->event_poll_fd.revents & G_IO_IN) retval = (_gdk_event_queue_find_first (event_source->display) != NULL || gdk_check_xpending (event_source->display)); -- 2.30.2